/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.exec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.omg.Dynamic.Parameter;

import combi.sipper.io.IOAssociation;
import combi.sipper.io.IOList;
import combi.sipper.io.IOQueriesFile;
import combi.sipper.modeling.distances.DistanceBetweenGenes;
import combi.sipper.modeling.distances.DistanceColocalisation;
import combi.utils.Couple;
import combi.wip.sipper.io.IOGenome;
import combi.wip.sipper.io.IOMetabolicNetworkWithEnzymes;
import combi.wip.sipper.io.metacyc.IOGenomeFromMetaCyc;
import combi.wip.sipper.io.metacyc.IOMetabolicNetworkWithEnzymesFromMetaCyc;
import combi.wip.sipper.modeling.Protein;
import combi.wip.sipper.modeling.genomes.Gene;
import combi.wip.sipper.modeling.genomes.Genome;
import combi.wip.sipper.modeling.reactions.Reaction;

public class GenerateKSIPsEndPoints {

	/**
	 * Create a file that contains the set of all requested k-SIPs.
	 * Constraints about requested k-SIPs must take place in parameters.
	 * In this version, the cartesien product between elements in the entry files is computed 
	 */

	public static void main (String[] args)
	{
		String format = args[0]; // l: list, or i : implication
		String inFile = args[1];
		String outFile = args[2];
		String filtering = args[3]; //none or geneDistance=XX or reactionDistance=XX
		IOQueriesFile out = new IOQueriesFile(outFile);
		try {
			out.write(filter(load(inFile,format), filtering, args));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private static List<Couple<List<String>, List<String>>> filter(
			List<Couple<List<String>, List<String>>> list, String filtering, String[] args) throws IOException {

		String[] parameters = filtering.split("=");
		//System.err.println(Arrays.asList(parameters));
		if (parameters[0].equalsIgnoreCase("none"))
		{
			return list;
		}
		if (parameters[0].equalsIgnoreCase("geneDistance"))
		{		
			Integer dist = new Integer(parameters[1]);
			// besoin genome, distance entre genes et lien gene/reaction
			String fileGenome = args[4];
			String fileProteins = args[6];
			IOGenome<Gene<Protein>, Protein> genomeIO = new IOGenomeFromMetaCyc<Gene<Protein>>(fileGenome, true, fileProteins);
			genomeIO.load();
			Genome<Gene<Protein>,Protein> genome = genomeIO.get();
			DistanceColocalisation<Gene<Protein>, Protein> distance = new DistanceColocalisation<Gene<Protein>, Protein>(genome);

			String fileAssociation = args[5];
			Map<String, Set<String>> asso = IOAssociation.loadString(fileAssociation, "([^\t]+)\t(.+)+");


			List<Couple<List<String>, List<String>>> result = new ArrayList<Couple<List<String>, List<String>>>();
			int total = list.size();
			int enCours = 0;
			for (Couple<List<String>, List<String>> c : list)
			{
				boolean ok = false;
				//on calcule la distance min entre les elements de la liste

				ListIterator<String> it1 = c.getFirst().listIterator();
				while (!ok && it1.hasNext())
				{
					String elem1 = it1.next();
					ListIterator<String> it2 = c.getSecond().listIterator();
					while (!ok && it2.hasNext())
					{
						String elem2 = it2.next();
						//System.err.println(elem1 + "->" + elem2);
						Set<String> s1 = asso.get(elem1);
						if (s1 != null)
						{
							Iterator<String> sIt1 = s1.iterator();
							while (!ok && sIt1.hasNext())
							{
								String g1 = sIt1.next();
								//System.err.println(elem1+ " " + g1+ " " + genome.getGene(g1));
								Set<String> s2 = asso.get(elem2);
								if (s2 != null)
								{
									Iterator<String> sIt2 = s2.iterator();
									while (!ok && sIt2.hasNext())
									{
										String g2 = sIt2.next();
										//System.err.println(elem2+ " " + g2+ " " + genome.getGene(g2));
										//System.err.println(genome.getGene(g1)+ " -> " + genome.getGene(g2) + " = " + distance.get(genome.getGene(g1), genome.getGene(g2)));
										ok = distance.get(genome.getGene(g1), genome.getGene(g2)) <= dist.doubleValue();
									}
								}
							}
						}
					}
				}
				if(ok)
				{
					result.add(c);
				}
				enCours++;
				//System.out.println(enCours*100/total+"%");
			}
			//System.err.println(result);
			//System.err.println(genome);
			//System.err.println(asso);
			return result;	
		}
		if (parameters[0].equalsIgnoreCase("reactionDistance"))
		{
			System.err.println("Error: not implemented!");
			Integer dist = Integer.getInteger(parameters[1]);
			//besoin reseau de reaction et du plus court chemin
		}
		return null;
	}

	public static List<Couple<List<String>, List<String>>> load(String fileIn, String format) throws IOException
	{
		if (format.equals("l"))
		{
			IOList reader = new IOList(fileIn);
			List<String> list = reader.read();
			return prodCartesien(list, list, false);
		}
		if (format.equals("i"))
		{
			IOQueriesFile reader = new IOQueriesFile(fileIn);
			return reader.read();
		}
		return null;
	}

	public static List<Couple<List<String>, List<String>>> prodCartesien(List<String> l1, List<String> l2, boolean diag)
	{
		List<Couple<List<String>, List<String>>> result = new ArrayList<Couple<List<String>, List<String>>>(l1.size()*l2.size());
		for (String e1 : l1)
		{
			for (String e2 :l2)
			{
				if (diag || !e1.equals(e2))
				{
					result.add(new Couple<List<String>, List<String>>(Collections.singletonList(e1),Collections.singletonList(e2)));
				}
			}
		}
		return result;
	}


}
